package org.vsoc.modules.license.service.impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.crypto.asymmetric.KeyType;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.vsoc.license.LicenseCheckModel;
import org.vsoc.license.creator.LicenseCreator;
import org.vsoc.license.creator.LicenseCreatorParam;
import org.vsoc.license.decoder.MachineCodeDecoder;
import org.vsoc.license.enums.MachineCodeFormatVersion;
import org.vsoc.modules.license.entity.License;
import org.vsoc.modules.license.mapper.LicenseMapper;
import org.vsoc.modules.license.service.ILicenseService;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Description: license操作
 * @Author: jeecg-boot
 * @Date:   2025-06-26
 * @Version: V1.0
 */
@Slf4j
@Service
public class LicenseServiceImpl extends ServiceImpl<LicenseMapper, License> implements ILicenseService {
    @javax.annotation.Resource
    private ResourceLoader resourceLoader;

    @Value("${license.subject}")
    private String subject;
    @Value("${license.PrivateAlias}")
    private String privateAlias;
    @Value("${license.KeyPass}")
    private String keyPass;
    @Value("${license.StorePass}")
    private String storePass;
    @Value("${license.ConsumerType}")
    private String consumerType;


    @Value("${jeecg.path.upload}")
    private String uploadPath;
    @Autowired
    private LicenseMapper licenseMapper;

    private final String PRIKEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAI54NlsGPFoB/Iy9JATmAtmXgwcQ2d5aD3CY7PKUM3jU6dJZ9UwVxiY9j90LNIHjb1veZfP7eh55DHqqrrP/j5iZTqzpLYJO4mS1kt6EymlMLOrm8TOE8CIxVsPP7OiFoOYqqfRLprmHwWaYbPy7m1gPrV49bEv9oLBSnalJI7UPAgMBAAECgYALp4+BbGX/0kgdIT8y5rgBTHuBAU1j+vJmunPZDOuVrQvVqTCVAUunhti1Hedf4BbqKSwmRIJYaibtr/cWWB0gdiiV57mIuI7eT+N8qqrfLtG4hmps8Reo/1G4P9y0szLJSrdrqaasHD5Cse7DKiea6pSxN2utXPoF4ZKUHy69+QJBAMhnmreAPtuBNfjbFEukALJkIVCseKnYMjNnrZdspJnE3ZaVO+BpndMiIYzBE4quFngdLGO2O7exg3wpB0burYkCQQC1/ifRfFhqt29GqeZXUZkvnXHzJWmE+mAcno9MAmYx7zjKa05qqmW3Ip6DbrzLd6J4M6O08rmxkey0Li7kkX/XAkB+CXKrj7N4l6XbEeWMwONTTlgJhb8Jipff7oelYUWQE9kMuGSE+cexnpidDlz87gnhlu9w8o5SNAkukBssiDWJAkAsz8TsLwryU/KBWspNy9jFQaZSMW8XADN0OJKeK6UQPJAmP1/SodxjyJFxWW4BR5botAZmQrH4iC7ZetmvDCiHAkAYb2ZuhRbq55K3qB9xENt53fGVuJCO4O/aapUnMdxU91bS7o3QqO9VoOxOckoQDfawRZ8Eovg/xA9DGwn0R46y";

    /**
     * 生成License文件
     * @param license
     */
    @Override
    public Boolean generateLicense(License license) throws Exception {
        //标准化本地临时文件夹路径
        Assert.notNull(uploadPath, "uploadPath is null");
        final String SIEM_LICENSE_DIRNAME = "SimeLicense";
        String standardUploadPath = this.uploadPath.replace("\\", File.separator);
        if (!standardUploadPath.endsWith(File.separator)) {
            standardUploadPath += File.separator;
        }
        standardUploadPath += SIEM_LICENSE_DIRNAME;
        File uploadPathFile = new File(standardUploadPath);
        if (!uploadPathFile.exists()) {
            uploadPathFile.mkdirs();
        }
        Resource priviteKeyFileResource = resourceLoader.getResource("classpath:license/SIEMPrivateKey.keystore");
        Resource publicKeyFileResource = resourceLoader.getResource("classpath:license/SIEMPublicCerts.keystore");
//        Resource priviteKeyFileResource = new ClassPathResource("SIEMPrivateKey.keystore");
//        Resource publicKeyFileResource = new ClassPathResource("SIEMPublicCerts.keystore");

        // 检查密钥库文件是否存在
        if (!priviteKeyFileResource.exists()) {
            throw new IOException("SIEMPrivateKey.keystore 文件不存在");
        }
        if (!publicKeyFileResource.exists()) {
            throw new IOException("SIEMPublicCerts.keystore 文件不存在");
        }

        // 生成一个新的临时目录名称
        Path tempDirPath = Paths.get(System.getProperty("java.io.tmpdir"));
        //--------------------------------
//        String absolutePath = Files.createTempDirectory(tempDirPath, ".temp").toFile().getAbsolutePath();
//        if (!absolutePath.endsWith(File.separator)) {
//            absolutePath = absolutePath + File.separator;
//            log.info("generateZipLicense absolutePath:{}", absolutePath);
//        }
//
//        File privateKeyFile = new File(absolutePath + "SIEMPrivateKey.keystore");
//        File publicKeyFile = new File(absolutePath + "SIEMPublicCerts.keystore");
//---------------------------------------------
        // 新代码：用 Paths.resolve 构建临时文件路径
        // 创建临时目录（前缀明确，方便调试）
        Path tempDir = Files.createTempDirectory("SIEM_KeyStore_");
        log.info("临时目录已创建: {}", tempDir);
        // 构建密钥库文件路径（自动处理分隔符）
        Path privateKeyPath = tempDir.resolve("SIEMPrivateKey.keystore");
        Path publicKeyPath = tempDir.resolve("SIEMPublicCerts.keystore");
        File privateKeyFile = privateKeyPath.toFile();
        File publicKeyFile = publicKeyPath.toFile();
        // 复制文件
        try {
            log.info("开始复制私钥文件到: {}", privateKeyFile.getAbsolutePath());

            FileUtils.copyToFile(priviteKeyFileResource.getInputStream(),new File(privateKeyFile.toPath().toString()));

//            IOUtils.copy(priviteKeyFileResource.getInputStream(), Files.newOutputStream(privateKeyFile.toPath()));
            log.info("私钥文件复制成功");


            log.info("开始复制公钥文件到: {}", publicKeyFile.getAbsolutePath());
            FileUtils.copyToFile(publicKeyFileResource.getInputStream(),new File(publicKeyFile.toPath().toString()));
//            IOUtils.copy(publicKeyFileResource.getInputStream(), Files.newOutputStream(publicKeyFile.toPath()));
            log.info("公钥文件复制成功");
        } catch (IOException e) {
            log.error("复制密钥库文件时出错", e);
            throw e;
        }
        //-------------------------------
        // 标准化上传根路径（自动处理跨平台分隔符）
        Path uploadRoot = Paths.get(this.uploadPath)
                .toAbsolutePath() // 转为绝对路径
                .normalize();      // 标准化（解析..等）
//  构建最终的上传目录：uploadRoot/SIEM_LICENSE_DIRNAME
        Path licenseBaseDir = uploadRoot.resolve(SIEM_LICENSE_DIRNAME);
// 确保目录存在（递归创建）
        Files.createDirectories(licenseBaseDir);

// 构建 license 子目录（含UUID）
        String licenseSubDirName = UUID.fastUUID().toString().replace("-", "");
        Path licenseDir = licenseBaseDir.resolve(licenseSubDirName);
        Files.createDirectories(licenseDir);
        String licenseDirPath = licenseDir.toString(); // 最终路径（跨平台规范）
        //-----------------------------------
//        String licenseDirPath = standardUploadPath + File.separator + UUID.fastUUID().toString().replace("-", "");
        File licenseDirPathFile = new File(licenseDirPath);
        if (!licenseDirPathFile.exists()) {
            boolean mkdirs = licenseDirPathFile.mkdirs();
            log.debug("mkdir {} result={}", licenseDirPath, mkdirs);
        }

        // 获取相对于 uploadRoot 的路径。将相对路径存入库中
        Path relativeLicenseDir = uploadRoot.relativize(licenseDir);
        String relativeLicenseDirPath = relativeLicenseDir.toString(); // 例如：SimeLicense\abc123456789
        license.setLicenseRelativePath(relativeLicenseDirPath);

        String machineCode = license.getMachineCode();
        String licenseName = license.getLicenseName();
        Date issuedTime = license.getIssuedTime();
        Date expiryTime = license.getExpiryTime();
        String mssp = license.getMssp();
        Boolean flag = true;
        if (Objects.nonNull(machineCode) && !machineCode.trim().isEmpty()) {
            final String LICENSE_FILE_NAME = "SimeLicense.lic";
            // 用 Paths 构建最终 License 文件路径
            //-------------------------------------------------
            Path licenseFilePath = licenseDir.resolve(LICENSE_FILE_NAME);
            String licenseFile1 = licenseFilePath.toString(); // 跨平台规范路径
            //-----------------------------------------------
//            final String licenseFile1 = licenseDirPath + File.separator + LICENSE_FILE_NAME;
//            final String subject = "anxinsiem";
            LicenseCreatorParam param = new LicenseCreatorParam();
            param.setSubject(subject);
            param.setPrivateAlias(privateAlias);
            param.setKeyPass(keyPass);
            param.setStorePass(storePass);
            param.setLicensePath(licenseFile1);
            param.setPrivateKeysStorePath(privateKeyFile.getAbsolutePath());
            param.setIssuedTime(issuedTime);
            param.setExpiryTime(expiryTime);
            param.setConsumerType(consumerType);
            param.setConsumerAmount(1);
            param.setDescription(mssp);
            System.out.println("msspFunctionJson=" + mssp);

            ObjectMapper objectMapper = new ObjectMapper();

            LicenseCheckModel licenseCheckModel = null;
            if (machineCode.startsWith("BEGIN")) {
                String encodeServerInfo = machineCode.replaceAll("^BEGIN", "").replaceAll("END$", "");
                cn.hutool.crypto.asymmetric.RSA rsaPrikey = new cn.hutool.crypto.asymmetric.RSA(PRIKEY, null);
                byte[] decrypt = rsaPrikey.decrypt(Base64.decodeBase64(encodeServerInfo), KeyType.PrivateKey);
                String serverInfo = new String(decrypt);
                licenseCheckModel = objectMapper.readValue(serverInfo, LicenseCheckModel.class);
                licenseCheckModel.setFormatVersion(MachineCodeFormatVersion.START_WITH_BEGIN);
            } else if (machineCode.startsWith("###") && machineCode.endsWith("###")) {
                String serverInfo = MachineCodeDecoder.decode(machineCode);
                licenseCheckModel = objectMapper.readValue(serverInfo, LicenseCheckModel.class);
                licenseCheckModel.setFormatVersion(MachineCodeFormatVersion.START_WITH_THREE_HASH);
            } else throw new RuntimeException("许可证生成失败:machineCode格式错误！");

            param.setLicenseCheckModel(licenseCheckModel);
            LicenseCreator licenseCreator = new LicenseCreator(param);
            boolean result = licenseCreator.generateLicense();
            flag = result;
            if (result) {
                // 获取 License 文件路径和目标目录
                String licenseFilePath1 = param.getLicensePath();
                File licenseFile = new File(licenseFilePath1);
                File targetDirectory = licenseFile.getParentFile();

                // 构建 ZIP 文件路径
////                String zipFileName = licenseFile.getName().replace(".lic", ".zip");
//                String zipFileName = "licensev2.lic";
//                File zipFile = new File(targetDirectory, zipFileName);

                // 构建 ZIP 文件路径
//                String zipFileName = "license_backage.zip";
                String zipFileName = "license.lic";
                File zipFile = new File(targetDirectory, zipFileName);

                // 创建 ZIP 输出流
                try (FileOutputStream fos = new FileOutputStream(zipFile);
                     ZipOutputStream zos = new ZipOutputStream(fos)) {

                    // 添加 License 文件到 ZIP
                    addFileToZip(zos, licenseFile, licenseFile.getName());

                    // 添加 SIEM 公钥证书文件到 ZIP
                    File publicCertFile = publicKeyFile;
                    if (publicCertFile.exists()) {
                        addFileToZip(zos, publicCertFile, publicCertFile.getName());
                    } else {
                        System.err.println("公钥证书文件不存在: " + publicCertFile.getAbsolutePath());
                        flag = false;
                        throw new RuntimeException("公钥证书文件不存在: " + publicCertFile.getAbsolutePath());
                    }
////                     添加 licensev2.lic 到 ZIP
//                    File licenseV2File = new File(targetDirectory, "licensev2.lic");
//                    if (licenseV2File.exists()) {
//                        addFileToZip(zos, licenseV2File, "licensev2.lic");  // 显式指定内部文件名
//                    } else {
//                        System.err.println("licensev2.lic 文件不存在: " + licenseV2File.getAbsolutePath());
//                        throw new RuntimeException("licensev2.lic 文件不存在: " + licenseV2File.getAbsolutePath());
//                    }
                    zos.close();
                    fos.close();
                }finally {
                    if(licenseFile.exists()){
                        licenseFile.delete();
                    }
                    //关闭资源
                    FileUtils.deleteDirectory(tempDir.toFile());
                    privateKeyFile.delete();
                    publicKeyFile.delete();
                }
                //根据id保存
                log.info("设置前值为："+license.getLicenseFileGenerated());
                license.setLicenseFileGenerated(1);
                log.info("设置后值为："+license.getLicenseFileGenerated());
                licenseMapper.updateById(license);
                licenseFile.deleteOnExit();
                log.info("更新后值为："+license.getLicenseFileGenerated());
                System.out.println("证书生成成功！ZIP 文件已创建：" + zipFile.getAbsolutePath());

            } else {
                System.out.println("证书生成失败！");
                throw new RuntimeException("证书生成失败！");
            }
        }
        //关闭资源
        FileUtils.deleteDirectory(tempDir.toFile());
        privateKeyFile.delete();
        publicKeyFile.delete();
        return flag;

    }

    // 辅助方法：将文件添加到 ZIP 包中
    private void addFileToZip(ZipOutputStream zos, File file, String entryName) throws IOException {
        try (FileInputStream fis = new FileInputStream(file)) {
            ZipEntry zipEntry = new ZipEntry(entryName);
            zos.putNextEntry(zipEntry);

            byte[] bytes = new byte[1024];
            int length;
            while ((length = fis.read(bytes)) >= 0) {
                zos.write(bytes, 0, length);
            }
            zos.closeEntry();
        }
    }

    /**
     * 下载文件
     *
     * @param fileName 文件名
     * @param license license对象
     * @param request HttpServletRequest对象
     * @param response HttpServletResponse对象
     */
    @Override
    public void downloadFile(String fileName, License license, HttpServletRequest request, HttpServletResponse response) {
        InputStream inputStream = null;
        OutputStream outputStream = null;
//        String zipName = "license_package.zip";
        String zipName = "license.lic";
        License licenseValue = licenseMapper.selectById(license.getId());
        String filePath = licenseValue.getLicenseRelativePath();

        try {
            // 构建完整的文件路径
            Path path = Paths.get(uploadPath, filePath);
            //path后再加上zipName
            path = path.resolve(zipName);
            File file = path.toFile();

            // 检查文件是否存在
            if (!file.exists() || !file.isFile()) {
                response.setContentType("text/html;charset=utf-8");
                response.getWriter().write("文件不存在或已被删除！");
                return;
            }

            // 确定下载的文件名
            if (fileName == null || "".equals(fileName)) {
//                fileName ="license_package.zip";
                fileName ="license.lic";
            }

            // 设置响应头
            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename=" +
                    URLEncoder.encode(fileName, "UTF-8"));
            response.setHeader("Content-Length", String.valueOf(file.length()));

            // 读取文件并输出到响应流
            inputStream = new FileInputStream(file);
            outputStream = response.getOutputStream();
            FileCopyUtils.copy(inputStream, outputStream);
            outputStream.flush();
        } catch (Exception e) {
            log.error("文件下载失败", e);
            throw new RuntimeException("文件下载失败", e);
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (Exception e) {
                log.error("关闭流失败", e);
                throw new RuntimeException("文件下载失败", e);
            }
        }
    }


}